home *** CD-ROM | disk | FTP | other *** search
- <?php
- // +---------------------------------------------------------------------+
- // | PHP Version 4 |
- // +---------------------------------------------------------------------+
- // | Copyright (c) 1997, 1998, 1999, 2000, 2001 The PHP Group |
- // +---------------------------------------------------------------------+
- // | This source file is subject to version 2.0 of the PHP license, |
- // | that is bundled with this package in the file LICENSE, and is |
- // | available at through the world-wide-web at |
- // | http://www.php.net/license/2_02.txt. |
- // | If you did not receive a copy of the PHP license and are unable to |
- // | obtain it through the world-wide-web, please send a note to |
- // | license@php.net so we can mail you a copy immediately. |
- // +---------------------------------------------------------------------+
- // | Author: Bertrand Mansion <bmansion@mamasam.com> |
- // +---------------------------------------------------------------------+
- //
- // $Id: Container.php,v 1.33 2004/06/14 11:08:31 mansion Exp $
-
- require_once('Config.php');
-
- /**
- * Interface for Config containers
- *
- * @author Bertrand Mansion <bmansion@mamasam.com>
- * @package Config
- */
- class Config_Container {
-
- /**
- * Container object type
- * Ex: section, directive, comment, blank
- * @var string
- */
- var $type;
-
- /**
- * Container object name
- * @var string
- */
- var $name = '';
-
- /**
- * Container object content
- * @var string
- */
- var $content = '';
-
- /**
- * Container object children
- * @var array
- */
- var $children = array();
-
- /**
- * Reference to container object's parent
- * @var object
- */
- var $parent;
-
- /**
- * Array of attributes for this item
- * @var array
- */
- var $attributes;
-
- /**
- * Unique id to differenciate nodes
- *
- * This is used to compare nodes
- * Will not be needed anymore when this class will use ZendEngine 2
- *
- * @var int
- */
- var $_id;
-
- /**
- * Constructor
- *
- * @param string $type Type of container object
- * @param string $name Name of container object
- * @param string $content Content of container object
- * @param array $attributes Array of attributes for container object
- */
- function Config_Container($type = 'section', $name = '', $content = '', $attributes = null)
- {
- $this->type = $type;
- $this->name = $name;
- $this->content = $content;
- $this->attributes = $attributes;
- $this->parent = null;
- $this->_id = uniqid($name.$type, true);
- } // end constructor
-
- /**
- * Create a child for this item.
- * @param string $type type of item: directive, section, comment, blank...
- * @param mixed $item item name
- * @param string $content item content
- * @param array $attributes item attributes
- * @param string $where choose a position 'bottom', 'top', 'after', 'before'
- * @param object $target needed if you choose 'before' or 'after' for where
- * @return object reference to new item or Pear_Error
- */
- function &createItem($type, $name, $content, $attributes = null, $where = 'bottom', $target = null)
- {
- $item =& new Config_Container($type, $name, $content, $attributes);
- $result =& $this->addItem($item, $where, $target);
- return $result;
- } // end func &createItem
-
- /**
- * Adds an item to this item.
- * @param object $item a container object
- * @param string $where choose a position 'bottom', 'top', 'after', 'before'
- * @param object $target needed if you choose 'before' or 'after' in $where
- * @return mixed reference to added container on success, Pear_Error on error
- */
- function &addItem(&$item, $where = 'bottom', $target = null)
- {
- if ($this->type != 'section') {
- return PEAR::raiseError('Config_Container::addItem must be called on a section type object.', null, PEAR_ERROR_RETURN);
- }
- if (is_null($target)) {
- $target =& $this;
- }
- if (strtolower(get_class($target)) != 'config_container') {
- return PEAR::raiseError('Target must be a Config_Container object in Config_Container::addItem.', null, PEAR_ERROR_RETURN);
- }
-
- switch ($where) {
- case 'before':
- $index = $target->getItemIndex();
- break;
- case 'after':
- $index = $target->getItemIndex()+1;
- break;
- case 'top':
- $index = 0;
- break;
- case 'bottom':
- $index = -1;
- break;
- default:
- return PEAR::raiseError('Use only top, bottom, before or after in Config_Container::addItem.', null, PEAR_ERROR_RETURN);
- }
- if (isset($index) && $index >= 0) {
- array_splice($this->children, $index, 0, 'tmp');
- } else {
- $index = count($this->children);
- }
- $this->children[$index] =& $item;
- $this->children[$index]->parent =& $this;
-
- return $item;
- } // end func addItem
-
- /**
- * Adds a comment to this item.
- * This is a helper method that calls createItem
- *
- * @param string $content Object content
- * @param string $where Position : 'top', 'bottom', 'before', 'after'
- * @param object $target Needed when $where is 'before' or 'after'
- * @return object reference to new item or Pear_Error
- */
- function &createComment($content = '', $where = 'bottom', $target = null)
- {
- return $this->createItem('comment', null, $content, null, $where, $target);
- } // end func &createComment
-
- /**
- * Adds a blank line to this item.
- * This is a helper method that calls createItem
- *
- * @return object reference to new item or Pear_Error
- */
- function &createBlank($where = 'bottom', $target = null)
- {
- return $this->createItem('blank', null, null, null, $where, $target);
- } // end func &createBlank
-
- /**
- * Adds a directive to this item.
- * This is a helper method that calls createItem
- *
- * @param string $name Name of new directive
- * @param string $content Content of new directive
- * @param mixed $attributes Directive attributes
- * @param string $where Position : 'top', 'bottom', 'before', 'after'
- * @param object $target Needed when $where is 'before' or 'after'
- * @return object reference to new item or Pear_Error
- */
- function &createDirective($name, $content, $attributes = null, $where = 'bottom', $target = null)
- {
- return $this->createItem('directive', $name, $content, $attributes, $where, $target);
- } // end func &createDirective
-
- /**
- * Adds a section to this item.
- *
- * This is a helper method that calls createItem
- * If the section already exists, it won't create a new one.
- * It will return reference to existing item.
- *
- * @param string $name Name of new section
- * @param array $attributes Section attributes
- * @param string $where Position : 'top', 'bottom', 'before', 'after'
- * @param object $target Needed when $where is 'before' or 'after'
- * @return object reference to new item or Pear_Error
- */
- function &createSection($name, $attributes = null, $where = 'bottom', $target = null)
- {
- return $this->createItem('section', $name, null, $attributes, $where, $target);
- } // end func &createSection
-
- /**
- * Tries to find the specified item(s) and returns the objects.
- *
- * Examples:
- * $directives =& $obj->getItem('directive');
- * $directive_bar_4 =& $obj->getItem('directive', 'bar', null, 4);
- * $section_foo =& $obj->getItem('section', 'foo');
- *
- * This method can only be called on an object of type 'section'.
- * Note that root is a section.
- * This method is not recursive and tries to keep the current structure.
- * For a deeper search, use searchPath()
- *
- * @param string $type Type of item: directive, section, comment, blank...
- * @param mixed $name Item name
- * @param mixed $content Find item with this content
- * @param array $attributes Find item with attribute set to the given value
- * @param int $index Index of the item in the returned object list. If it is not set, will try to return the last item with this name.
- * @return mixed reference to item found or false when not found
- * @see &searchPath()
- */
- function &getItem($type = null, $name = null, $content = null, $attributes = null, $index = -1)
- {
- if ($this->type != 'section') {
- return PEAR::raiseError('Config_Container::getItem must be called on a section type object.', null, PEAR_ERROR_RETURN);
- }
- if (!is_null($type)) {
- $testFields[] = 'type';
- }
- if (!is_null($name)) {
- $testFields[] = 'name';
- }
- if (!is_null($content)) {
- $testFields[] = 'content';
- }
- if (!is_null($attributes) && is_array($attributes)) {
- $testFields[] = 'attributes';
- }
-
- $itemsArr = array();
- $fieldsToMatch = count($testFields);
- for ($i = 0, $count = count($this->children); $i < $count; $i++) {
- $match = 0;
- reset($testFields);
- foreach ($testFields as $field) {
- if ($field != 'attributes') {
- if ($this->children[$i]->$field == ${$field}) {
- $match++;
- }
- } else {
- // Look for attributes in array
- $attrToMatch = count($attributes);
- $attrMatch = 0;
- foreach ($attributes as $key => $value) {
- if (isset($this->children[$i]->attributes[$key]) &&
- $this->children[$i]->attributes[$key] == $value) {
- $attrMatch++;
- }
- }
- if ($attrMatch == $attrToMatch) {
- $match++;
- }
- }
- }
- if ($match == $fieldsToMatch) {
- $itemsArr[] =& $this->children[$i];
- }
- }
- if ($index >= 0) {
- if (isset($itemsArr[$index])) {
- return $itemsArr[$index];
- } else {
- return false;
- }
- } else {
- if ($count = count($itemsArr)) {
- return $itemsArr[$count-1];
- } else {
- return false;
- }
- }
- } // end func &getItem
-
- /**
- * Finds a node using XPATH like format:
- *
- * search format = array ( item,item,item,item)
- * item = 'string' .. will match the <string of the xml element
- * item = array('string',array('name'=>'xyz'))
- * .. will match <string name="xyz">
- *
- * @param mixed Search path and attributes
- *
- * @return mixed Config_Container object, array of Config_Container objects or false on failure.
- * @access public
- */
- function &searchPath($args)
- {
- if ($this->type != 'section') {
- return PEAR::raiseError('Config_Container::searchPath must be called on a section type object.', null, PEAR_ERROR_RETURN);
- }
-
- $arg = array_shift($args);
-
- if (is_array($arg)) {
- $name = $arg[0];
- $attributes = $arg[1];
- } else {
- $name = $arg;
- $attributes = null;
- }
- // find all the matches for first..
- $match =& $this->getItem(null, $name, null, $attributes);
-
- if (!$match) {
- return false;
- }
- if (!empty($args)) {
- return $match->searchPath($args);
- }
- return $match;
- } // end func &searchPath
-
- /**
- * Returns how many children this container has
- *
- * @param string $type type of children counted
- * @param string $name name of children counted
- * @return int number of children found
- */
- function countChildren($type = null, $name = null)
- {
- if ($this->type != 'section') {
- return PEAR::raiseError('Config_Container::getChildrenNum must be called on a section type object.', null, PEAR_ERROR_RETURN);
- }
- if (is_null($type) && is_null($name)) {
- return count($this->children);
- }
- $count = 0;
- if (isset($name) && isset($type)) {
- for ($i = 0, $children = count($this->children); $i < $children; $i++) {
- if ($this->children[$i]->name == $name &&
- $this->children[$i]->type == $type) {
- $count++;
- }
- }
- return $count;
- }
- if (isset($type)) {
- for ($i = 0, $children = count($this->children); $i < $children; $i++) {
- if ($this->children[$i]->type == $type) {
- $count++;
- }
- }
- return $count;
- }
- if (isset($name)) {
- // Some directives can have the same name
- for ($i = 0, $children = count($this->children); $i < $children; $i++) {
- if ($this->children[$i]->name == $name) {
- $count++;
- }
- }
- return $count;
- }
- } // end func &countChildren
-
- /**
- * Deletes an item (section, directive, comment...) from the current object
- * TODO: recursive remove in sub-sections
- * @return mixed true if object was removed, false if not, or PEAR_Error if root
- */
- function removeItem()
- {
- if ($this->isRoot()) {
- return PEAR::raiseError('Cannot remove root item in Config_Container::removeItem.', null, PEAR_ERROR_RETURN);
- }
- $index = $this->getItemIndex();
- if (!is_null($index)) {
- array_splice($this->parent->children, $index, 1);
- return true;
- }
- return false;
- } // end func removeItem
-
- /**
- * Returns the item index in its parent children array.
- * @return int returns int or null if root object
- */
- function getItemIndex()
- {
- if (is_object($this->parent)) {
- // This will be optimized with Zend Engine 2
- $pchildren =& $this->parent->children;
- for ($i = 0, $count = count($pchildren); $i < $count; $i++) {
- if ($pchildren[$i]->_id == $this->_id) {
- return $i;
- }
- }
- }
- return;
- } // end func getItemIndex
-
- /**
- * Returns the item rank in its parent children array
- * according to other items with same type and name.
- * @return int returns int or null if root object
- */
- function getItemPosition()
- {
- if (is_object($this->parent)) {
- $pchildren =& $this->parent->children;
- for ($i = 0, $count = count($pchildren); $i < $count; $i++) {
- if ($pchildren[$i]->name == $this->name &&
- $pchildren[$i]->type == $this->type) {
- $obj[] =& $pchildren[$i];
- }
- }
- for ($i = 0, $count = count($obj); $i < $count; $i++) {
- if ($obj[$i]->_id == $this->_id) {
- return $i;
- }
- }
- }
- return;
- } // end func getItemPosition
-
- /**
- * Returns the item parent object.
- * @return object returns reference to parent object or null if root object
- */
- function &getParent()
- {
- return $this->parent;
- } // end func &getParent
-
- /**
- * Returns the item parent object.
- * @return mixed returns reference to child object or false if child does not exist
- */
- function &getChild($index = 0)
- {
- if (!empty($this->children[$index])) {
- return $this->children[$index];
- } else {
- return false;
- }
- } // end func &getChild
-
- /**
- * Set this item's name.
- * @return void
- */
- function setName($name)
- {
- $this->name = $name;
- } // end func setName
-
- /**
- * Get this item's name.
- * @return string item's name
- */
- function getName()
- {
- return $this->name;
- } // end func getName
-
- /**
- * Set this item's content.
- * @return void
- */
- function setContent($content)
- {
- $this->content = $content;
- } // end func setContent
-
- /**
- * Get this item's content.
- * @return string item's content
- */
- function getContent()
- {
- return $this->content;
- } // end func getContent
-
- /**
- * Set this item's type.
- * @return void
- */
- function setType($type)
- {
- $this->type = $type;
- } // end func setType
-
- /**
- * Get this item's type.
- * @return string item's type
- */
- function getType()
- {
- return $this->type;
- } // end func getType
-
- /**
- * Set this item's attributes.
- * @param array $attributes Array of attributes
- * @return void
- */
- function setAttributes($attributes)
- {
- $this->attributes = $attributes;
- } // end func setAttributes
-
- /**
- * Set this item's attributes.
- * @param array $attributes Array of attributes
- * @return void
- */
- function updateAttributes($attributes)
- {
- if (is_array($attributes)) {
- foreach ($attributes as $key => $value) {
- $this->attributes[$key] = $value;
- }
- }
- } // end func updateAttributes
-
- /**
- * Get this item's attributes.
- * @return array item's attributes
- */
- function getAttributes()
- {
- return $this->attributes;
- } // end func getAttributes
-
- /**
- * Get one attribute value of this item
- * @param string $attribute Attribute key
- * @return mixed item's attribute value
- */
- function getAttribute($attribute)
- {
- if (isset($this->attributes[$attribute])) {
- return $this->attributes[$attribute];
- }
- return null;
- } // end func getAttribute
-
- /**
- * Set a children directive content.
- * This is an helper method calling getItem and addItem or setContent for you.
- * If the directive does not exist, it will be created at the bottom.
- *
- * @param string $name Name of the directive to look for
- * @param mixed $content New content
- * @param int $index Index of the directive to set,
- * in case there are more than one directive
- * with the same name
- * @return object newly set directive
- */
- function &setDirective($name, $content, $index = -1)
- {
- $item =& $this->getItem('directive', $name, null, null, $index);
- if ($item === false || PEAR::isError($item)) {
- // Directive does not exist, will create one
- unset($item);
- return $this->createDirective($name, $content, null);
- } else {
- // Change existing directive value
- $item->setContent($content);
- return $item;
- }
- } // end func setDirective
-
- /**
- * Is this item root, in a config container object
- * @return bool true if item is root
- */
- function isRoot()
- {
- if (is_null($this->parent)) {
- return true;
- }
- return false;
- } // end func isRoot
-
- /**
- * Call the toString methods in the container plugin
- * @param string $configType Type of configuration used to generate the string
- * @param array $options Specify special options used by the parser
- * @return mixed true on success or PEAR_ERROR
- */
- function toString($configType, $options = array())
- {
- $configType = strtolower($configType);
- if (!isset($GLOBALS['CONFIG_TYPES'][$configType])) {
- return PEAR::raiseError("Configuration type '$configType' is not registered in Config_Container::toString.", null, PEAR_ERROR_RETURN);
- }
- $includeFile = $GLOBALS['CONFIG_TYPES'][$configType][0];
- $className = $GLOBALS['CONFIG_TYPES'][$configType][1];
- include_once($includeFile);
- $renderer = new $className($options);
- return $renderer->toString($this);
- } // end func toString
-
- /**
- * Returns a key/value pair array of the container and its children.
- *
- * Format : section[directive][index] = value
- * If the container has attributes, it will use '@' and '#'
- * index is here because multiple directives can have the same name.
- *
- * @param bool $useAttr Whether to return the attributes too
- * @return array
- */
- function toArray($useAttr = true)
- {
- $array[$this->name] = array();
- switch ($this->type) {
- case 'directive':
- if ($useAttr && count($this->attributes) > 0) {
- $array[$this->name]['#'] = $this->content;
- $array[$this->name]['@'] = $this->attributes;
- } else {
- $array[$this->name] = $this->content;
- }
- break;
- case 'section':
- if ($useAttr && count($this->attributes) > 0) {
- $array[$this->name]['@'] = $this->attributes;
- }
- if ($count = count($this->children)) {
- for ($i = 0; $i < $count; $i++) {
- $newArr = $this->children[$i]->toArray($useAttr);
- if (!is_null($newArr)) {
- foreach ($newArr as $key => $value) {
- if (isset($array[$this->name][$key])) {
- // duplicate name/type
- if (!is_array($array[$this->name][$key]) ||
- !isset($array[$this->name][$key][0])) {
- $old = $array[$this->name][$key];
- unset($array[$this->name][$key]);
- $array[$this->name][$key][0] = $old;
- }
- $array[$this->name][$key][] = $value;
- } else {
- $array[$this->name][$key] = $value;
- }
- }
- }
- }
- }
- break;
- default:
- return null;
- }
- return $array;
- } // end func toArray
-
- /**
- * Writes the configuration to a file
- *
- * @param mixed $datasrc Info on datasource such as path to the configuraton file or dsn...
- * @param string $configType Type of configuration
- * @param array $options Options for writer
- * @access public
- * @return mixed true on success or PEAR_ERROR
- */
- function writeDatasrc($datasrc, $configType, $options = array())
- {
- $configType = strtolower($configType);
- if (!isset($GLOBALS['CONFIG_TYPES'][$configType])) {
- return PEAR::raiseError("Configuration type '$configType' is not registered in Config_Container::writeDatasrc.", null, PEAR_ERROR_RETURN);
- }
- $includeFile = $GLOBALS['CONFIG_TYPES'][$configType][0];
- $className = $GLOBALS['CONFIG_TYPES'][$configType][1];
- include_once($includeFile);
-
- $writeMethodName = (version_compare(phpversion(), '5', '<')) ? 'writedatasrc' : 'writeDatasrc';
- if (in_array($writeMethodName, get_class_methods($className))) {
- $writer = new $className($options);
- return $writer->writeDatasrc($datasrc, $this);
- }
-
- // Default behaviour
- $fp = @fopen($datasrc, 'w');
- if ($fp) {
- $string = $this->toString($configType, $options);
- $len = strlen($string);
- @flock($fp, LOCK_EX);
- @fwrite($fp, $string, $len);
- @flock($fp, LOCK_UN);
- @fclose($fp);
- return true;
- } else {
- return PEAR::raiseError('Cannot open datasource for writing.', 1, PEAR_ERROR_RETURN);
- }
- } // end func writeDatasrc
- } // end class Config_Container
- ?>